package com.dyson.singleton;

/**
 * @ClassName: DCLSingleton
 * @Date: 2020/7/20 15:13
 * @Author: WY
 * @Email: dysonwy@foxmail.com
 * @Desc: 双重检测锁
 * 线程安全的单例模式：
 *  用double-check-lock（DCL）实现
 *
 *  在创建对象时，分为了3步：
 *  1、在堆中给DCLSingleton 的实例分配内存；
 *  2、初始化DCLSingleton 的构造器；
 *  3、将instance对象指向分配的内存空间（注意到这步instance就非null了）
 *
 *  由于2,3没有依赖性,运行期间处理器会进行重排序，我们有可能是1->2->3也有可能是1->3->2
 *  即我们有可能在先返回instance实例，然后执行构造方法。
 *  即：double-check-locking可能存在线程拿到一个没有执行构造方法的对象(重排序后构造没执行完),
 *  或者在构造器中，出现重排序后，构造器还没有执行完毕，但是呢这个instance已经不是null了，
 *  然后另一条线程拿到这个instance并且调用该实例的成员变量的方法，就会出现空指针异常。
 *  (instance先执行完,构造并未完成对变量赋值,此时使用变量会有NPE异常)
 *
 *  添加volatile禁止重排序,解决问题
 */
public class DCLSingleton {
    private DCLSingleton() {
        s1 = "1";
        s2 = "2";
        s3 = "3";
        s4 = "4";
    }

    private String s1;
    private String s2;
    private String s3;
    private String s4;
    private static volatile DCLSingleton singleton = null;

    public static DCLSingleton getInstance() {
        if (singleton == null) {
            synchronized (DCLSingleton.class) {
                if (singleton == null) {
                    singleton = new DCLSingleton();
                }
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
        DCLSingleton s1 = getInstance();
        DCLSingleton s2 = getInstance();
        System.out.println(s1 == s2);
    }
}
